package com.fish.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fish.common.exception.BusinessException;
import com.fish.common.util.RedisUtil;
import com.fish.dao.mapper.MenuMapper;
import com.fish.dao.mapper.RoleMenuMapper;
import com.fish.model.dto.MenuDTO;
import com.fish.model.entity.Menu;
import com.fish.model.entity.RoleMenu;
import com.fish.model.vo.MenuVO;
import com.fish.service.MenuService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 菜单服务实现类
 */
@Slf4j
@Service
public class MenuServiceImpl implements MenuService {
    
    @Autowired
    private MenuMapper menuMapper;
    
    @Autowired
    private RoleMenuMapper roleMenuMapper;
    
    @Autowired
    private RedisUtil redisUtil;
    
    // Redis缓存key前缀
    private static final String USER_MENUS_KEY = "user:menus:";
    private static final String USER_PERMISSIONS_KEY = "user:permissions:";
    private static final long CACHE_EXPIRE_SECONDS = 3600; // 缓存1小时
    
    @Override
    @SuppressWarnings("unchecked")
    public List<MenuVO> getMenuTreeByUserId(Long userId) {
        // 1. 先从Redis缓存获取
        String cacheKey = USER_MENUS_KEY + userId;
        Object cached = redisUtil.get(cacheKey);
        if (cached != null) {
            log.debug("从Redis缓存获取用户菜单，userId: {}", userId);
            return (List<MenuVO>) cached;
        }
        
        // 2. 缓存未命中，查询数据库
        log.debug("缓存未命中，从数据库查询用户菜单，userId: {}", userId);
        List<Menu> menus = menuMapper.selectMenusByUserId(userId);
        
        // 3. 转换为VO并构建树形结构
        List<MenuVO> menuTree = buildMenuTree(menus, 0L);
        
        // 4. 存入Redis缓存
        redisUtil.set(cacheKey, menuTree, CACHE_EXPIRE_SECONDS);
        
        return menuTree;
    }
    
    @Override
    @SuppressWarnings("unchecked")
    public Set<String> getPermissionsByUserId(Long userId) {
        // 1. 先从Redis缓存获取
        String cacheKey = USER_PERMISSIONS_KEY + userId;
        Object cached = redisUtil.get(cacheKey);
        if (cached != null) {
            log.debug("从Redis缓存获取用户权限，userId: {}", userId);
            return (Set<String>) cached;
        }
        
        // 2. 缓存未命中，查询数据库
        log.debug("缓存未命中，从数据库查询用户权限，userId: {}", userId);
        List<String> permissions = menuMapper.selectPermissionsByUserId(userId);
        Set<String> permissionSet = new HashSet<>(permissions);
        
        // 3. 存入Redis缓存
        redisUtil.set(cacheKey, permissionSet, CACHE_EXPIRE_SECONDS);
        
        return permissionSet;
    }
    
    @Override
    public List<MenuVO> getAllMenuTree() {
        LambdaQueryWrapper<Menu> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Menu::getStatus, 1);
        wrapper.orderByAsc(Menu::getOrderNum);
        
        List<Menu> menus = menuMapper.selectList(wrapper);
        return buildMenuTree(menus, 0L);
    }
    
    @Override
    public List<Long> getMenuIdsByRoleId(Long roleId) {
        LambdaQueryWrapper<RoleMenu> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RoleMenu::getRoleId, roleId);
        
        List<RoleMenu> roleMenus = roleMenuMapper.selectList(wrapper);
        return roleMenus.stream()
                .map(RoleMenu::getMenuId)
                .collect(Collectors.toList());
    }
    
    /**
     * 构建菜单树
     *
     * @param menus 菜单列表
     * @param parentId 父菜单ID
     * @return 菜单树
     */
    private List<MenuVO> buildMenuTree(List<Menu> menus, Long parentId) {
        List<MenuVO> tree = new ArrayList<>();
        
        for (Menu menu : menus) {
            if (menu.getParentId().equals(parentId)) {
                MenuVO menuVO = new MenuVO();
                BeanUtil.copyProperties(menu, menuVO);
                
                // 递归查找子菜单
                List<MenuVO> children = buildMenuTree(menus, menu.getId());
                if (!children.isEmpty()) {
                    menuVO.setChildren(children);
                }
                
                tree.add(menuVO);
            }
        }
        
        // 按orderNum排序
        tree.sort(Comparator.comparing(MenuVO::getOrderNum));
        
        return tree;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public MenuVO addMenu(MenuDTO menuDTO) {
        // 创建菜单
        Menu menu = new Menu();
        BeanUtil.copyProperties(menuDTO, menu);
        menu.setVisible(menuDTO.getVisible() != null ? menuDTO.getVisible() : 1);
        menu.setStatus(menuDTO.getStatus() != null ? menuDTO.getStatus() : 1);
        menu.setOrderNum(menuDTO.getOrderNum() != null ? menuDTO.getOrderNum() : 0);
        
        int result = menuMapper.insert(menu);
        if (result <= 0) {
            throw new BusinessException("添加菜单失败");
        }
        
        log.info("添加菜单成功，菜单名称：{}", menu.getMenuName());
        
        MenuVO menuVO = new MenuVO();
        BeanUtil.copyProperties(menu, menuVO);
        return menuVO;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public MenuVO updateMenu(MenuDTO menuDTO) {
        // 1. 检查菜单是否存在
        Menu menu = menuMapper.selectById(menuDTO.getId());
        if (menu == null) {
            throw new BusinessException("菜单不存在");
        }
        
        // 2. 更新菜单信息
        BeanUtil.copyProperties(menuDTO, menu);
        
        int result = menuMapper.updateById(menu);
        if (result <= 0) {
            throw new BusinessException("更新菜单失败");
        }
        
        log.info("更新菜单成功，菜单ID：{}", menu.getId());
        
        MenuVO menuVO = new MenuVO();
        BeanUtil.copyProperties(menu, menuVO);
        return menuVO;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMenu(Long menuId) {
        // 1. 检查菜单是否存在
        Menu menu = menuMapper.selectById(menuId);
        if (menu == null) {
            throw new BusinessException("菜单不存在");
        }
        
        // 2. 检查是否有子菜单
        LambdaQueryWrapper<Menu> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Menu::getParentId, menuId);
        Long count = menuMapper.selectCount(wrapper);
        if (count > 0) {
            throw new BusinessException("该菜单下还有子菜单，无法删除");
        }
        
        // 3. 删除菜单
        int result = menuMapper.deleteById(menuId);
        if (result <= 0) {
            throw new BusinessException("删除菜单失败");
        }
        
        // 4. 删除角色菜单关联
        LambdaQueryWrapper<RoleMenu> roleMenuWrapper = new LambdaQueryWrapper<>();
        roleMenuWrapper.eq(RoleMenu::getMenuId, menuId);
        roleMenuMapper.delete(roleMenuWrapper);
        
        // 5. 清除所有用户的菜单和权限缓存
        clearAllUserCache();
        
        log.info("删除菜单成功，菜单ID：{}", menuId);
    }
    
    /**
     * 清除所有用户的菜单和权限缓存
     */
    private void clearAllUserCache() {
        log.info("清除所有用户的菜单和权限缓存");
        redisUtil.deleteByPattern(USER_MENUS_KEY + "*");
        redisUtil.deleteByPattern(USER_PERMISSIONS_KEY + "*");
    }
    
    /**
     * 清除指定用户的菜单和权限缓存
     */
    public void clearUserCache(Long userId) {
        log.info("清除用户缓存，userId: {}", userId);
        redisUtil.delete(USER_MENUS_KEY + userId);
        redisUtil.delete(USER_PERMISSIONS_KEY + userId);
    }
}